﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Xml;
using DiscoveryLogic.Analytics.DataObjects.Visuals;
using DiscoveryLogic.Common.Utilities;
using DiscoveryLogic.Workflows.Components.Entities;
using DiscoveryLogic.Workflows.Components.Entities.Distances.Comparers;

namespace DiscoveryLogic.Analytics.DataObjects.Rules
{
    public class EntityDedupRule:RuleBase
    {
        #region props
        private Dictionary<string,double> _DescAttrWeights;
        /// <summary></summary>
        public Dictionary<string,double> DescAttrWeights
        {
            get
            {
                return this._DescAttrWeights;
            }
            set
            {
                this._DescAttrWeights = value;
            }
        }

        private Dictionary<string,double> _AssocAttrWeights;
        /// <summary></summary>
        public Dictionary<string,double> AssocAttrWeights
        {
            get
            {
                return this._AssocAttrWeights;
            }
            set
            {
                this._AssocAttrWeights = value;
            }
        }

        private Dictionary<string,IAttributeComparer> _Comparers;
        /// <summary></summary>
        public Dictionary<string, IAttributeComparer> Comparers
        {
            get
            {
                return this._Comparers;
            }
            set
            {
                this._Comparers = value;
            }
        }

        private Dictionary<string,double> _Penalties;
        /// <summary></summary>
        public Dictionary<string,double> Penalties
        {
            get
            {
                return this._Penalties;
            }
            set
            {
                this._Penalties = value;
            }
        }

        private double _AssocThreshold;
        /// <summary></summary>
        public double AssocThreshold
        {
            get
            {
                return this._AssocThreshold;
            }
            set
            {
                this._AssocThreshold = value;
            }
        }

        private double _CollapseThreshold;
        /// <summary></summary>
        public double CollapseThreshold
        {
            get
            {
                return this._CollapseThreshold;
            }
            set
            {
                this._CollapseThreshold = value;
            }
        }
        #endregion

        public EntityDedupRule(string ruleName, RuleType ruleType, 
            SetDataSourceType srcDataFormat, EntityType srcEntityType, 
            EntityType tgtEntityType, string iconPath) 
            : base(ruleName, ruleType, srcDataFormat, 
            srcEntityType, tgtEntityType, iconPath)
        {
            this._DescAttrWeights=new Dictionary<string, double>();
            this._AssocAttrWeights=new Dictionary<string, double>();
            this._Comparers=new Dictionary<string, IAttributeComparer>();
            this._Penalties=new Dictionary<string, double>();
            this._AssocThreshold = 0;
            this._CollapseThreshold = 0;
        }

        #region Overrides of RuleBase

        public override void Save(ref XmlDocument xDoc, XmlNode ruleNode)
        {
            ruleNode.RemoveAll();
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "RuleName", this.RuleName);
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "RuleType", this.RuleType.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "IconPath", this.IconPath);

            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "SrcDataFormat", this.SrcDataFormat.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "SrcEntityType", this.SrcEntityType.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "TgtEntityType", this.TgtEntityType.ToString());

            XmlNode descWeightNodes = XmlDataUtil.AddElement(ref xDoc, ruleNode, "DescWeights");
            if(this.DescAttrWeights !=null && this.DescAttrWeights.Count>0)
            {
                foreach(string fieldName in this.DescAttrWeights.Keys)
                {
                    XmlNode descWeightNode = XmlDataUtil.AddElement(ref xDoc, descWeightNodes, "DescWeight");
                    XmlDataUtil.UpdateAttribute(ref xDoc, descWeightNode, "FieldName",fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, descWeightNode,"Weight", this.DescAttrWeights[fieldName].ToString());
                }
            }
            XmlNode assocWeightNodes = XmlDataUtil.AddElement(ref xDoc, ruleNode, "AssocWeights");
            if(this.AssocAttrWeights !=null && this.AssocAttrWeights.Count>0)
            {
                foreach(string fieldName in this.AssocAttrWeights.Keys)
                {
                    XmlNode assocWeightNode = XmlDataUtil.AddElement(ref xDoc, assocWeightNodes, "AssocWeight");
                    XmlDataUtil.UpdateAttribute(ref xDoc, assocWeightNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, assocWeightNode, "Weight", this.AssocAttrWeights[fieldName].ToString());
                }
            }
            XmlNode compareNodes = XmlDataUtil.AddElement(ref xDoc, ruleNode, "Comparers");
            if(this.Comparers !=null && this.Comparers.Count>0)
            {
                foreach(string fieldName in this.Comparers.Keys)
                {
                    XmlNode compareNode = XmlDataUtil.AddElement(ref xDoc, compareNodes, "Comparer");
                    XmlDataUtil.UpdateAttribute(ref xDoc, compareNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, compareNode, "Comparer", this.Comparers[fieldName].GetType().FullName);
                }
            }
            XmlNode penaltyNodes = XmlDataUtil.AddElement(ref xDoc, ruleNode, "Penalties");
            if(this.Penalties !=null && this.Penalties.Count>0)
            {
                foreach(string fieldName in this.Penalties.Keys)
                {
                    XmlNode penaltyNode = XmlDataUtil.AddElement(ref xDoc, penaltyNodes, "Penalty");
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode, "FieldName", fieldName);
                    XmlDataUtil.UpdateAttribute(ref xDoc, penaltyNode, "Penalty", this.Penalties[fieldName].ToString());
                }
            }

            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "AssocThreshold", this.AssocThreshold.ToString());
            XmlDataUtil.UpdateAttribute(ref xDoc, ruleNode, "CollapseThreshold",this.CollapseThreshold.ToString());
        }

        public override RuleBase Read(XmlNode ruleNode)
        {
            EntityDedupRule rule=new EntityDedupRule(string.Empty, RuleType.Dedup,
                SetDataSourceType.DB, EntityType.Organizations,
                EntityType.Organizations, EntityIcons.DedupRuleIcon);

            rule.RuleName = XmlDataUtil.GetAttributeValue(ruleNode, "RuleName", "");
            rule.RuleType =
                (RuleType)
                Enum.Parse(typeof (RuleType),
                           XmlDataUtil.GetAttributeValue(ruleNode, "RuleType", RuleType.Dedup.ToString()));
            rule.IconPath = XmlDataUtil.GetAttributeValue(ruleNode, "IconPath", "");
            rule.SrcDataFormat =
                (SetDataSourceType)
                Enum.Parse(typeof(SetDataSourceType), XmlDataUtil.GetAttributeValue(ruleNode, "SrcDataFormat", ""));
            rule.SrcEntityType =
                (EntityType)
                Enum.Parse(typeof (EntityType),
                           XmlDataUtil.GetAttributeValue(ruleNode, "SrcEntityType", EntityType.Grants.ToString()));
            rule.TgtEntityType =
                (EntityType)
                Enum.Parse(typeof(EntityType),
                           XmlDataUtil.GetAttributeValue(ruleNode, "TgtEntityType", EntityType.Grants.ToString()));

            rule.DescAttrWeights=new Dictionary<string, double>();
            XmlNodeList descWeightNodes = ruleNode.SelectNodes("DescWeights/DescWeight");
            if(descWeightNodes !=null && descWeightNodes.Count>0)
            {
                foreach(XmlNode descWeightNode in descWeightNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(descWeightNode, "FieldName", "");
                    double weight = double.Parse(XmlDataUtil.GetAttributeValue(descWeightNode, "Weight", "0"));
                    rule.DescAttrWeights.Add(fieldName,weight);
                }
            }
            rule.AssocAttrWeights=new Dictionary<string, double>();
            XmlNodeList assocWeightNodes = ruleNode.SelectNodes("AssocWeights/AssocWeight");
            if(assocWeightNodes !=null && assocWeightNodes.Count>0)
            {
                foreach(XmlNode assocWeightNode in assocWeightNodes)
                {
                    string fieldName = XmlDataUtil.GetAttributeValue(assocWeightNode, "FieldName", "");
                    double weight = double.Parse(XmlDataUtil.GetAttributeValue(assocWeightNode, "Weight", "0"));
                    rule.AssocAttrWeights.Add(fieldName, weight);
                }
            }
            rule.Comparers=new Dictionary<string, IAttributeComparer>();
            XmlNodeList comparerNodes = ruleNode.SelectNodes("Comparers/Comparer");
            Assembly assembly = typeof (IAttributeComparer).Assembly;
            foreach(XmlNode comparerNode in comparerNodes)
            {
                string fieldName = XmlDataUtil.GetAttributeValue(comparerNode, "FieldName", "");
                string typeName = XmlDataUtil.GetAttributeValue(comparerNode, "Comparer", "");
                Type comparerType = assembly.GetType(typeName);
                if (comparerType != null)
                {
                    IAttributeComparer comparer = (IAttributeComparer) Activator.CreateInstance(comparerType);
                    rule.Comparers.Add(fieldName, comparer);
                }
            }
            rule.Penalties=new Dictionary<string, double>();
            XmlNodeList penaltyNodes = ruleNode.SelectNodes("Penalties/Penalty");
            foreach(XmlNode penaltyNode in penaltyNodes)
            {
                string fieldName = XmlDataUtil.GetAttributeValue(penaltyNode, "FieldName", "");
                double penalty = double.Parse(XmlDataUtil.GetAttributeValue(penaltyNode, "Penalty", "0"));
                rule.Penalties.Add(fieldName,penalty);
            }

            rule.AssocThreshold = double.Parse(XmlDataUtil.GetAttributeValue(ruleNode, "AssocThreshold", "0"));
            rule.CollapseThreshold = double.Parse(XmlDataUtil.GetAttributeValue(ruleNode, "CollapseThreshold", "0"));

            return rule;
        }

        #endregion
    }
}
